Skip to content

Optimize StrictMetricsEvaluator reference binding and add benchmarks#823

Open
SURYAS1306 wants to merge 2 commits into
apache:mainfrom
SURYAS1306:filter-hotpath-benchmark-690
Open

Optimize StrictMetricsEvaluator reference binding and add benchmarks#823
SURYAS1306 wants to merge 2 commits into
apache:mainfrom
SURYAS1306:filter-hotpath-benchmark-690

Conversation

@SURYAS1306

Copy link
Copy Markdown

Summary

This PR implements the filtering hot-path optimization and benchmark infrastructure proposed in #690.

  • Replace RETURN_IF_NOT_REFERENCE with BIND_REFERENCE_OR_RETURN in StrictMetricsEvaluator so predicate handlers reuse the dynamic_cast result instead of calling expr->reference() (which bumps shared_ptr refcounts via shared_from_this()).
  • Add an off-by-default ICEBERG_BUILD_BENCHMARKS CMake option that fetches Google Benchmark (v1.9.1) and builds strict_metrics_evaluator_bench.

Motivation

StrictMetricsEvaluator runs once per data file during scan planning. The previous macro discarded a successful dynamic_cast and then re-fetched the same reference through a virtual call + atomic refcount operation on every predicate handler (14+ predicate types).

Benchmark results (Release, Apple Clang 17, macOS)

Micro-benchmark model of the predicate binding hot path:

Benchmark Before After Gain
Single predicate 21.0 ns 12.4 ns 1.69×
14 preds / file 206 ns 113 ns 1.82×
20 preds / file 294 ns 160 ns 1.84×

End-to-end StrictMetricsEvaluator::Evaluate benchmark (with binding + evaluation):

Predicates Time
1 ~137 ns
14 ~2.3 µs
20 ~3.3 µs

Test plan

  • expression_test --gtest_filter='StrictMetrics*' (42 tests pass)
  • Build with -DICEBERG_BUILD_BENCHMARKS=ON
  • Run ./benchmark/strict_metrics_evaluator_bench

Closes #690

Surya Srinivasan and others added 2 commits July 8, 2026 11:14
Reuse dynamic_cast results in predicate handlers to avoid redundant
shared_ptr refcount bumps, and add an optional Google Benchmark target
behind ICEBERG_BUILD_BENCHMARKS for the filtering hot path (issue apache#690).

Co-authored-by: Cursor <cursoragent@cursor.com>
Apply clang-format and cmake-format, move benchmarks under
src/iceberg/benchmark, and enable ICEBERG_BUILD_BENCHMARKS in the
cpp-linter workflow so Google Benchmark headers are available.

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes StrictMetricsEvaluator’s predicate reference binding by reusing the dynamic_cast result (avoiding repeated shared_from_this() refcount bumps), and adds an off-by-default benchmark build option plus an initial Google Benchmark target to measure the filtering hot path.

Changes:

  • Replace the RETURN_IF_NOT_REFERENCE pattern with BIND_REFERENCE_OR_RETURN so each predicate handler reuses the already-casted BoundReference*.
  • Add ICEBERG_BUILD_BENCHMARKS CMake option and a src/iceberg/benchmark/ subdirectory that fetches Google Benchmark and builds strict_metrics_evaluator_bench.
  • Enable ICEBERG_BUILD_BENCHMARKS=ON in the cpp-linter workflow build step to ensure the benchmark target stays buildable in CI.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/iceberg/expression/strict_metrics_evaluator.cc Hot-path optimization: reuse dynamic_cast result instead of calling reference() repeatedly.
CMakeLists.txt Add ICEBERG_BUILD_BENCHMARKS option (default OFF).
src/iceberg/CMakeLists.txt Wire benchmark subdir behind ICEBERG_BUILD_BENCHMARKS.
src/iceberg/benchmark/CMakeLists.txt Fetch Google Benchmark and define the benchmark executable target.
src/iceberg/benchmark/strict_metrics_evaluator_bench.cc Add initial micro-benchmark for StrictMetricsEvaluator::Evaluate.
.github/workflows/cpp-linter.yml Turn on benchmarks during CI build to catch build regressions.

Comment on lines +119 to +120
BENCHMARK(iceberg::BM_StrictMetricsEvaluate)->Arg(1)->Arg(14)->Arg(20);
BENCHMARK_MAIN();
Comment on lines +65 to +66
std::shared_ptr<Expression> MakeFilter(int predicate_count) {
std::vector<std::shared_ptr<Expression>> predicates;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a minimal micro-benchmark suite to validate filtering hot-path optimizations

2 participants